Skip to content

🤖 feat: add CloudNativePG-backed CoderControlPlane example#38

Merged
ThomasK33 merged 2 commits into
mainfrom
operator-deploy-55sw
Feb 10, 2026
Merged

🤖 feat: add CloudNativePG-backed CoderControlPlane example#38
ThomasK33 merged 2 commits into
mainfrom
operator-deploy-55sw

Conversation

@ThomasK33
Copy link
Copy Markdown
Member

Summary
This PR adds a runnable multi-resource example that deploys a CoderControlPlane backed by CloudNativePG-managed PostgreSQL.

Background
The operator already supports database wiring through spec.extraEnv, so we can provide a realistic Coder + Postgres setup without CRD or controller changes. The goal is an easier end-to-end demo manifest set and walkthrough.

Implementation

  • Added examples/cloudnativepg/namespace.yaml to create a dedicated coder namespace.
  • Added examples/cloudnativepg/cnpg-cluster.yaml with a single-instance PVC-backed CNPG cluster (coder-db) that bootstraps database/user coder.
  • Added examples/cloudnativepg/codercontrolplane.yaml wiring:
    • CODER_PG_CONNECTION_URL from CNPG Secret coder-db-app key uri
    • CODER_ACCESS_URL=http://localhost:8080 for local kubectl port-forward smoke testing
  • Added examples/cloudnativepg/README.md with prerequisites, install steps (CNPG + controller), deploy steps, readiness checks, and first-user setup flow.
  • Added a root README.md examples link to discover the new CloudNativePG example.

Validation

  • make verify-vendor
  • make test
  • make build
  • make lint (fails in this workspace due pre-existing formatting drift under tmpfork/coder/**, unrelated to this change)

Risks
Low risk. This change is documentation/manifests only and does not modify controller runtime behavior or APIs. The main user caveat is that CODER_ACCESS_URL=http://localhost:8080 is intentionally scoped to local port-forward smoke tests.


📋 Implementation Plan

Plan: Example CoderControlPlane deployment backed by CloudNativePG

Context / Why

We want a realistic, easier demo of the coder-k8s operator that includes a managed PostgreSQL database.

CloudNativePG (CNPG) is a widely used Postgres operator that:

  • provisions a Postgres cluster backed by PVCs
  • creates an application Secret containing a ready-to-use connection string (uri)

The CoderControlPlane CR already supports arbitrary env injection via spec.extraEnv, so we can wire CNPG → Coder with no CRD changes.

Evidence (what we verified)

  • internal/controller/codercontrolplane_controller.go
    • Reconciles a Deployment and Service named exactly after the CoderControlPlane (metadata.name).
    • Runs coder server --http-address=0.0.0.0:3000 and exposes it via a Service port (default 80) → target port 3000.
    • Passes spec.extraEnv directly to the Coder container.
  • api/v1alpha1/codercontrolplane_types.go: spec.extraEnv and spec.service.{type,port,annotations} exist; no dedicated DB fields.
  • docs/how-to/deploy-controller.md: canonical install steps for CRDs + RBAC + controller deployment (in namespace coder-system).
  • ./tmpfork/coder/helm/coder/README.md + values.yaml (Coder upstream):
    • DB is configured via CODER_PG_CONNECTION_URL.
    • CODER_ACCESS_URL is expected in typical setups; Helm chart may default it, but our operator does not.
  • CNPG research (sub-agent): CNPG Cluster creates a <clusterName>-app Secret that includes key uri (Postgres URL) and a <clusterName>-rw Service.
  • Coder “first user” + access URL behavior (sub-agent):
    • Fresh deployments serve a /setup flow to create the first admin user.
    • No CODER_DISABLE_AUTH exists.
    • If CODER_ACCESS_URL is unset, Coder may attempt to create a temporary public tunnel (*.try.coder.app).

Implementation plan

1) Add a new example directory

Create a new example folder that is multi-resource and runnable via kubectl apply:

Proposed location: examples/cloudnativepg/

Files:

  • examples/cloudnativepg/README.md
  • examples/cloudnativepg/namespace.yaml
  • examples/cloudnativepg/cnpg-cluster.yaml
  • examples/cloudnativepg/codercontrolplane.yaml
  • (optional) examples/cloudnativepg/kustomization.yaml to enable kubectl apply -k.

Rationale: config/samples/ is good for single-CR “shape” examples, but this demo needs multiple components (namespace + CNPG cluster + CoderControlPlane).

2) CNPG: define a PVC-backed Postgres cluster

Add cnpg-cluster.yaml using CNPG’s Cluster CRD.

Minimal single-instance dev/demo setup:

apiVersion: postgresql.cnpg.io/v1
kind: Cluster
metadata:
  name: coder-db
  namespace: coder
spec:
  instances: 1
  storage:
    size: 5Gi
  bootstrap:
    initdb:
      database: coder
      owner: coder

Notes to include in README:

  • CNPG will create:
    • Secret: coder-db-app (contains uri, username, password, database, ...)
    • Service: coder-db-rw (primary read-write endpoint)
  • For a more “realistic” HA demo, users can increase spec.instances to 3.

3) coder-k8s: wire the CNPG Secret into CoderControlPlane

Add codercontrolplane.yaml that references the CNPG-generated Secret.

apiVersion: coder.com/v1alpha1
kind: CoderControlPlane
metadata:
  name: coder
  namespace: coder
spec:
  # Optional: omit `image` to use the operator default (ghcr.io/coder/coder:latest)
  replicas: 1
  service:
    type: ClusterIP
    port: 80
  extraEnv:
    - name: CODER_PG_CONNECTION_URL
      valueFrom:
        secretKeyRef:
          name: coder-db-app
          key: uri
    # See README section on port-forward vs. real ingress/LB URLs.
    - name: CODER_ACCESS_URL
      value: "http://localhost:8080"
Why CODER_ACCESS_URL=http://localhost:8080 is tricky
  • It makes the browser experience easy when you run:
    • kubectl -n coder port-forward svc/coder 8080:80
  • But any Coder components running inside the cluster (e.g., workspace agents) cannot reach localhost:8080 because localhost resolves to themselves.
  • Coder may also log warnings because it periodically checks ${CODER_ACCESS_URL}/healthz from inside the pod; with localhost + port-forward, that check cannot succeed in-cluster.

So this example should explicitly frame localhost as a UI smoke test.

If we want an end-to-end workspace demo, the README should describe the alternative:

  • expose Coder via Ingress / LoadBalancer / NodePort and set CODER_ACCESS_URL to that externally reachable address, or
  • leave CODER_ACCESS_URL unset and rely on Coder’s tunnel feature (requires outbound internet).

4) README: end-to-end walkthrough

The README should include:

  1. Prereqs

    • Kubernetes cluster
    • kubectl
    • helm (for CNPG install)
  2. Install CloudNativePG operator

    • Recommended (Helm):
      helm repo add cnpg https://cloudnative-pg.github.io/charts
      helm upgrade --install cnpg cnpg/cloudnative-pg --namespace cnpg-system --create-namespace
    • Alternative (plain manifest): apply a pinned CNPG release YAML from the CNPG GitHub releases.
  3. Install the coder-k8s controller

    • Follow docs/how-to/deploy-controller.md (summarized):
      kubectl create namespace coder-system
      kubectl apply -f config/crd/bases/
      kubectl apply -f deploy/rbac.yaml
      kubectl apply -f deploy/controller-deployment.yaml
      kubectl rollout status deployment/coder-k8s-controller -n coder-system
    • (Optional) Deploy the aggregated API server separately; not required for CoderControlPlane.
  4. Deploy the example

    • kubectl apply -f examples/cloudnativepg/ (or kubectl apply -k ... if we add kustomize)
    • Wait for Postgres:
      • kubectl -n coder wait --for=condition=Ready cluster/coder-db --timeout=...
    • Confirm the Secret exists:
      • kubectl -n coder get secret coder-db-app
  5. Port-forward to test the UI

    • kubectl -n coder port-forward svc/coder 8080:80
    • Open http://localhost:8080/setup to create the first admin user.
  6. Callouts / limitations

    • Port-forward mode is for UI validation; workspaces need a real reachable CODER_ACCESS_URL.

5) (Optional) Provide two kustomize overlays

If we want a smoother UX without editing YAML, add overlays:

  • examples/cloudnativepg/overlays/port-forward/CODER_ACCESS_URL=http://localhost:8080
  • examples/cloudnativepg/overlays/ingress-or-lb/ → placeholder CODER_ACCESS_URL=https://coder.example.com and service.type: LoadBalancer (or Ingress notes)

This is optional; we can start with a single manifest + README instructions and add overlays later if desired.

Acceptance criteria / validation

When implemented, we should be able to:

  • Apply manifests (client-side dry-run succeeds).
  • Observe CNPG cluster becomes Ready and creates coder-db-app with key uri.
  • Observe CoderControlPlane becomes Ready (deployment has ready replicas).
  • Port-forward and reach the Coder /setup page.

Non-goals

  • Building a fully production-ready Coder + CNPG configuration (backups, HA tuning, monitoring).
  • End-to-end workspace agent connectivity while using localhost as the access URL (requires a real external URL).

Generated with mux • Model: openai:gpt-5.3-codex • Thinking: xhigh


Generated with mux • Model: openai:gpt-5.3-codex • Thinking: xhigh • Cost: $1.12

Add a multi-resource CloudNativePG example under `examples/cloudnativepg`,
including namespace, CNPG `Cluster`, and `CoderControlPlane` manifests,
plus a walkthrough README for deployment and access.

---
_Generated with [`mux`](https://github.com/coder/mux) • Model: `openai:gpt-5.3-codex` • Thinking: `xhigh`_

---
_Generated with `mux` • Model: `openai:gpt-5.3-codex` • Thinking: `xhigh` • Cost: `$1.12`_

<!-- mux-attribution: model=openai:gpt-5.3-codex thinking=xhigh costs=1.12 -->
@ThomasK33
Copy link
Copy Markdown
Member Author

@codex review

Please review this PR for correctness and merge readiness.

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 8876f70be8

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread examples/cloudnativepg/README.md
Prefix the namespace manifest filename and document the ordering so
`kubectl apply -f examples/cloudnativepg/` succeeds on fresh clusters.

---
_Generated with [`mux`](https://github.com/coder/mux) • Model: `openai:gpt-5.3-codex` • Thinking: `xhigh`_

---
_Generated with `mux` • Model: `openai:gpt-5.3-codex` • Thinking: `xhigh` • Cost: `$1.12`_

<!-- mux-attribution: model=openai:gpt-5.3-codex thinking=xhigh costs=1.12 -->
@ThomasK33
Copy link
Copy Markdown
Member Author

@codex review

Addressed the namespace ordering issue by renaming namespace.yaml to 00-namespace.yaml and documenting the ordering note in the example README.

@chatgpt-codex-connector
Copy link
Copy Markdown

Codex Review: Didn't find any major issues. 🚀

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@ThomasK33 ThomasK33 added this pull request to the merge queue Feb 10, 2026
@ThomasK33
Copy link
Copy Markdown
Member Author

Merged via the queue into main with commit 7bbbf09 Feb 10, 2026
8 checks passed
@ThomasK33 ThomasK33 deleted the operator-deploy-55sw branch February 10, 2026 16:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant